package com.alexeyhanin.tapestry.goodies.util.select;

import static org.apache.tapestry5.ioc.internal.util.InternalUtils.capitalize;
import org.apache.tapestry5.ioc.services.PropertyAccess;
import org.apache.tapestry5.ioc.util.BodyBuilder;
import org.apache.tapestry5.model.MutableComponentModel;
import org.apache.tapestry5.services.ClassTransformation;
import org.apache.tapestry5.services.ComponentClassTransformWorker;
import org.apache.tapestry5.services.TransformMethodSignature;

import java.lang.reflect.Modifier;
import java.text.MessageFormat;
import java.util.List;

/**
 * @author aha
 * @since 2009.5.11
 *        $Id: InjectSelectModelWorker.java 16 2009-11-30 18:08:12Z gagarin61 $
 */
public class InjectSelectModelWorker implements ComponentClassTransformWorker {

    private PropertyAccess propertyAccess;

    public InjectSelectModelWorker(PropertyAccess propertyAccess) {
        this.propertyAccess = propertyAccess;
    }

    public void transform(ClassTransformation transformation, MutableComponentModel model) {
        final List<String> fields = transformation.findFieldsWithAnnotation(InjectSelectModel.class);
        for (String itemsField : fields) {
            final InjectSelectModel annotation =
                    transformation.getFieldAnnotation(itemsField, InjectSelectModel.class);

            final String accessField =
                    transformation.addField(Modifier.PRIVATE, PropertyAccess.class.getName(), "_access");

            transformation.injectField(accessField, propertyAccess);

            addSelectModelGetter(transformation, itemsField, annotation, accessField);
            addValueEncoderGetter(transformation, "valueEncoder", GenericValueEncoder.class, itemsField, annotation, accessField);
        }
    }

    private void addSelectModelGetter(ClassTransformation transformation, String itemsField,
                                      InjectSelectModel annotation, String accessFieldName) {
        final String label = annotation.label();
        final String labelFormat = annotation.labelFormat();

        final String methodName = MessageFormat.format("get{0}{1}", capitalize(itemsField), capitalize("selectModel"));
        final String returnTypeQualifiedName = GenericSelectModel.class.getName();
        final TransformMethodSignature signature =
                new TransformMethodSignature(Modifier.PUBLIC, returnTypeQualifiedName, methodName, null, null);

        final BodyBuilder builder = new BodyBuilder();
        builder.begin();
        final String code = MessageFormat.format("return new {0}(\"{1}\", \"{2}\", {3}, {4});",
                returnTypeQualifiedName, label, labelFormat, accessFieldName, itemsField);
        builder.addln(code);
        builder.end();

        transformation.addMethod(signature, builder.toString());
    }

    private void addValueEncoderGetter(ClassTransformation transformation, String attributeSuffix,
                                       Class<?> returnType, String itemsField, InjectSelectModel annotation,
                                       String accessFieldName) {
        final String id = annotation.id(); // TODO support composite-id
        final String methodName = MessageFormat.format("get{0}{1}", capitalize(itemsField), capitalize(attributeSuffix));
        final String returnTypeQualifiedName = returnType.getName();
        final TransformMethodSignature signature =
                new TransformMethodSignature(Modifier.PUBLIC, returnTypeQualifiedName, methodName, null, null);

        final BodyBuilder builder = new BodyBuilder();
        builder.begin();
        final String code = MessageFormat.format("return new {0}(\"{1}\", {2}, {3});",
                returnTypeQualifiedName, id, accessFieldName, itemsField);
        builder.addln(code);
        builder.end();

        transformation.addMethod(signature, builder.toString());
    }

}
